package com.binance.mbxgateway.web.service.impl;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import com.binance.account.api.SubUserApi;
import com.binance.account.vo.subuser.request.UserIdReq;
import com.binance.account.vo.user.request.GetUserRequest;
import com.binance.account.vo.user.response.GetUserEmailResponse;
import com.binance.autojob.annotation.MyScheduledJob;
import com.binance.distcache.distlock.DistLockManager;
import com.binance.distcache.distlock.IDistLock;
import com.binance.master.utils.BitUtils;
import com.binance.master.utils.ThreadUtils;
import com.binance.master.web.handlers.MessageHelper;
import com.binance.mbxgateway.data.mapper.ApiDeletedModelMapper;
import com.binance.mbxgateway.data.mapper.OldApiModelMapper;
import com.binance.mbxgateway.data.models.ApiDeletedModel;
import com.binance.mbxgateway.vo.request.api.ApiKeyModelRequest;
import com.binance.mbxgateway.vo.request.api.GetApiListRequest;
import com.binance.mbxgateway.vo.request.api.SearchDeletedApiRequest;
import com.rabbitmq.client.Channel;
import com.wenfex.common.cache.CacheBuilder;
import com.wenfex.common.cache.LoadingCache;
import io.shardingsphere.api.HintManager;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpStatusCodeException;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.binance.account.api.UserApi;
import com.binance.account.api.UserSecurityApi;
import com.binance.account.api.UserSecurityLogApi;
import com.binance.account.vo.security.UserSecurityLogVo;
import com.binance.account.vo.security.request.UserIdRequest;
import com.binance.account.vo.security.request.VarificationTwoRequest;
import com.binance.account.vo.user.UserInfoVo;
import com.binance.account.vo.user.response.GetUserResponse;
import com.binance.master.enums.AuthTypeEnum;
import com.binance.master.enums.TerminalEnum;
import com.binance.master.error.BusinessException;
import com.binance.master.error.GeneralCode;
import com.binance.master.models.APIRequest;
import com.binance.master.utils.CopyBeanUtils;
import com.binance.master.utils.DateUtils;
import com.binance.master.utils.HeaderUtils;
import com.binance.master.utils.LanguageUtils;
import com.binance.master.utils.StringUtils;
import com.binance.master.utils.WebUtils;
import com.binance.master.utils.security.EncryptionUtils;
import com.binance.mbxgateway.data.mapper.ApiModelMapper;
import com.binance.mbxgateway.data.mapper.OperateLogModelMapper;
import com.binance.mbxgateway.data.models.ApiModel;
import com.binance.mbxgateway.data.models.OperateLogModel;
import com.binance.mbxgateway.vo.ApiKeyVo;
import com.binance.mbxgateway.vo.request.api.DeleteAllApiKeyRequest;
import com.binance.mbxgateway.vo.request.api.DeleteApiKeyRequest;
import com.binance.mbxgateway.vo.request.api.DisableApiByIdRequest;
import com.binance.mbxgateway.vo.request.api.EnableApiCreateRequest;
import com.binance.mbxgateway.vo.request.api.EnableApiWithdrawRequest;
import com.binance.mbxgateway.vo.request.api.GetApisRequest;
import com.binance.mbxgateway.vo.request.api.ReSendMailRequest;
import com.binance.mbxgateway.vo.request.api.ReSendMailVerifyRequest;
import com.binance.mbxgateway.vo.request.api.SaveApiKeyRequest;
import com.binance.mbxgateway.vo.request.api.SearchApiRequest;
import com.binance.mbxgateway.vo.request.api.UpdateApiKeyRequest;
import com.binance.mbxgateway.vo.response.PagingResult;
import com.binance.mbxgateway.vo.response.api.ApiModelResponse;
import com.binance.mbxgateway.vo.response.api.EnableApiWithdrawResponse;
import com.binance.mbxgateway.vo.response.api.UpdateApiKeyResponse;
import com.binance.mbxgateway.web.service.IApiService;
import com.binance.mbxgateway.web.service.IMatchboxService;
import com.binance.mbxgateway.web.service.IOperateLogService;
import com.binance.mbxgateway.web.utils.Constants;
import com.binance.mbxgateway.web.utils.MatchboxReturnUtils;
import com.binance.mbxgateway.web.utils.RegexUtils;
import com.binance.messaging.api.msg.MsgApi;
import com.binance.messaging.api.msg.request.SendMsgRequest;
import com.binance.sysconf.service.SysConfigVarCacheService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class ApiServiceImpl extends BaseServiceImpl implements IApiService {

    private static final String IP = "0.0.0.0";
    private static final String ILLEGAL_CHAR = "[`~!#$^&*()=|{}':;',\\[\\].<>/?~！#￥……&*（）——|{}【】‘；：”“'。，、？\\\\]";
    private static final Pattern ILLEGAL_CHAR_PATTERN = Pattern.compile(ILLEGAL_CHAR);
    private String aesPass;

    @Autowired
    private SysConfigVarCacheService sysConfigVarCacheService;
    @Resource
    private UserSecurityApi userSecurityApi;
    @Resource
    private UserSecurityLogApi userSecurityLogApi;
    @Resource
    private MsgApi msgApi;
    @Resource
    private UserApi userApi;
    @Resource
    private SubUserApi subUserApi;
    @Autowired
    private IMatchboxService matchboxService;
    @Value("#{'${spring.profiles.active}'.endsWith('-prod')}")
    private boolean production;
    @Resource
    private ApiModelMapper apiModelMapper;
    @Resource
    private OldApiModelMapper oldApiModelMapper;
    @Resource
    private ApiDeletedModelMapper apiDeletedModelMapper;
    @Resource
    private OperateLogModelMapper operateLogModelMapper;
    @Resource
    private IOperateLogService operateLogService;
    @Resource
    private MessageHelper messageHelper;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private DistLockManager distLockManager;
    @Value("${api_manage_operation_old_db:true}")
    private boolean isUseOldDB;
    @Value("${api_manage_syncfom_old_db:true}")
    private boolean isSyncFromOldDB;

    @Value("${redis.addresses}")
    private String redisAddresses;
    @Value("${redis.pool.pass}")
    private String redisPassword;
    private LoadingCache<String, String> checkSumKey = null;
    private static LoadingCache<String, Long> apiManagePrimaryKey = null;
    private static final String API_MANAGE_PRIMARY_KEY_REDIS_KEY = "THE_API_MANAGE_DATA_PRIMARY_KEY";
    private static final String MBX_SYNC_API_MANAGE_MAX_ID = "mbx_sync_api_manage_max_id";
    @Value("${api_manage_init_id:5000000}")
    private long initId;

    @PostConstruct
    public void init() {
        checkSumKey = CacheBuilder.newBuilder().redisAddresses(redisAddresses).bufferSize(2048)
                .redisPassword(redisPassword).build();
        aesPass = checkSumKey.getCipherCode("CHECK_SUM_KEY", "binance");

        apiManagePrimaryKey = CacheBuilder.newBuilder().redisAddresses(redisAddresses).bufferSize(2048)
                .redisPassword(redisPassword).build();
        Long key = apiManagePrimaryKey.incr(API_MANAGE_PRIMARY_KEY_REDIS_KEY);
        if (key.longValue() < initId) {
            apiManagePrimaryKey.incr(API_MANAGE_PRIMARY_KEY_REDIS_KEY, initId - key.longValue());
        }
        // 测试环境下，每次启动服务时都要保持最大同步ID是0
        if (!this.production) {
            redisTemplate.opsForValue().set(MBX_SYNC_API_MANAGE_MAX_ID, 0);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public PagingResult<ApiModelResponse> loadApiList(SearchApiRequest body) throws Exception {
        boolean useOldDB = this.isUseOldDB;
        log.info("ApiServiceImpl.loadApiList,the request param is {}", body);
        if (StringUtils.isNotBlank(body.getApiKey()) && body.isFrontEnd()) {
            body.setApiKey(CryptoUtils.encryptAESToString(body.getApiKey(), this.aesPass));
        }
        if (body.getStartTime() == null) {
            body.setStartTime(DateUtils.getTodayBegin());
        }
        if (body.getEndTime() == null) {
            body.setEndTime(DateUtils.getTodayEnd());
        }
        if (body.getStart() == null || body.getStart() < 0) {
            body.setStart(0);
        }
        if (body.getOffset() == null || body.getOffset() < 0) {
            body.setOffset(0);
        }
        String userId = body.getUserId();
        String email = body.getEmail();
        if (StringUtils.isNotBlank(email)) {
            GetUserRequest getUserRequest = new GetUserRequest();
            getUserRequest.setEmail(body.getEmail());
            String theUserId = String.valueOf(
                    this.getAPIRequestResponse(this.userApi.getUserIdByEmail(this.newAPIRequest(getUserRequest))));
            if (StringUtils.isNotBlank(theUserId) && !StringUtils.equals(theUserId, "null")) {
                if (StringUtils.isNotBlank(userId)) {
                    if (!StringUtils.equals(userId, theUserId)) {
                        return new PagingResult<>(Lists.newArrayList(), 0);
                    }
                } else {
                    userId = theUserId;
                }
            } else {
                return new PagingResult<>(Lists.newArrayList(), 0);
            }
        }
        body.setUserId(userId);

        Map<String, Object> param = CopyBeanUtils.copy(body, Map.class);
        param.put("start", body.getStart());
        param.put("offset", body.getOffset());

        log.info("ApiServiceImpl.loadApiList,the param is {}", body);
        List<ApiModel> mapList =
                useOldDB ? this.oldApiModelMapper.selectApiList(param) : this.apiModelMapper.selectApiList(param);
        List<ApiModelResponse> list = Lists.newArrayList();
        for (ApiModel model : mapList) {
            String apiKey = model.getApiKey();
            if (StringUtils.isNotBlank(apiKey)) {
                model.setApiKey(CryptoUtils.decryptAESToString(apiKey, this.aesPass));
            }
            if (StringUtils.isBlank(model.getEmail())) {
                if (StringUtils.isBlank(email)) {
                    UserIdRequest userIdRequest = new UserIdRequest();
                    userIdRequest.setUserId(Long.parseLong(model.getUserId()));
                    GetUserEmailResponse getUserEmailResponse = this.getAPIRequestResponse(
                            this.userApi.getUserEmailByUserId(this.newAPIRequest(userIdRequest)));
                    if (getUserEmailResponse != null) {
                        model.setEmail(getUserEmailResponse.getEmail());
                    }
                } else {
                    model.setEmail(email);
                }
            }
            list.add(CopyBeanUtils.copy(model, ApiModelResponse.class));
        }
        return new PagingResult<ApiModelResponse>(list, useOldDB ? this.oldApiModelMapper.selectApiListCount(param)
                : this.apiModelMapper.selectApiListCount(param));
    }

    @Override
    public PagingResult<ApiModelResponse> loadDeletedApiList(SearchDeletedApiRequest body) throws Exception {
        if (body.getStartTime() == null) {
            body.setStartTime(DateUtils.getTodayBegin());
        }
        if (body.getEndTime() == null) {
            body.setEndTime(DateUtils.getTodayEnd());
        }
        if (body.getStart() == null || body.getStart() < 0) {
            body.setStart(0);
        }
        if (body.getOffset() == null || body.getOffset() < 0) {
            body.setOffset(0);
        }
        Map<String, Object> param = CopyBeanUtils.copy(body, Map.class);
        param.put("start", body.getStart());
        param.put("offset", body.getOffset());

        List<ApiDeletedModel> mapList = this.apiDeletedModelMapper.loadByParams(param);
        List<ApiModelResponse> list = Lists.newArrayList();
        for (ApiDeletedModel model : mapList) {
            String apiKey = model.getApiKey();
            if (StringUtils.isNotBlank(apiKey)) {
                model.setApiKey(CryptoUtils.decryptAESToString(apiKey, this.aesPass));
            }
            ApiModelResponse response = CopyBeanUtils.copy(model, ApiModelResponse.class);
            response.setId(model.getOriginalId().toString());
            list.add(response);
        }
        return new PagingResult<ApiModelResponse>(list, this.apiDeletedModelMapper.countByParams(param));
    }

    @Override
    public void addDeletedApi(ApiKeyModelRequest body) throws Exception {
        ApiDeletedModel deletedModel = CopyBeanUtils.copy(body, ApiDeletedModel.class);
        deletedModel.setOriginalId(body.getId());
        deletedModel.setId(null);
        deletedModel.setEmail(this.getUser(body.getUserId()).getUser().getEmail());
        this.apiDeletedModelMapper.insert(deletedModel);
    }

    @Override
    public ApiModelResponse queryApiByUserAndApiKey(String userId, String apiKey) throws Exception {
        boolean useOldDB = this.isUseOldDB;
        if (StringUtils.isBlank(userId)) {
            throw new BusinessException(GeneralCode.ILLEGAL_PARAM, "userId不能为空");
        }
        if (StringUtils.isBlank(apiKey)) {
            throw new BusinessException(GeneralCode.ILLEGAL_PARAM, "apiKey不能为空");
        }
        Map<String, Object> param = Maps.newHashMap();
        param.put("userId", userId);
        param.put("apiKey", CryptoUtils.encryptAESToString(apiKey, this.aesPass));
        List<ApiModel> mapList =
                useOldDB ? this.oldApiModelMapper.selectApiList(param) : this.apiModelMapper.selectApiList(param);
        if (mapList != null && mapList.size() > 0) {
            ApiModel model = mapList.get(0);
            if (StringUtils.isNotBlank(model.getApiKey())) {
                model.setApiKey(CryptoUtils.decryptAESToString(model.getApiKey(), this.aesPass));
            }
            if (StringUtils.isBlank(model.getEmail())) {
                UserIdRequest userIdRequest = new UserIdRequest();
                userIdRequest.setUserId(Long.parseLong(model.getUserId()));
                GetUserEmailResponse getUserEmailResponse = this
                        .getAPIRequestResponse(this.userApi.getUserEmailByUserId(this.newAPIRequest(userIdRequest)));
                if (getUserEmailResponse != null) {
                    model.setEmail(getUserEmailResponse.getEmail());
                }
            }
            return CopyBeanUtils.copy(model, ApiModelResponse.class);
        }
        return null;
    }

    private boolean isContainSpecialChar(String content) {
        return ILLEGAL_CHAR_PATTERN.matcher(content).find();
    }


    private static Long getPrimaryKey() {
        return apiManagePrimaryKey.incr(API_MANAGE_PRIMARY_KEY_REDIS_KEY);
    }

    @Override
    public ApiModelResponse saveApiKey(SaveApiKeyRequest body) throws Exception {
        boolean useOldDB = this.isUseOldDB;
        if (isContainSpecialChar(body.getApiName())) {
            throw new BusinessException(GeneralCode.SYS_VALID, "apiName包含非法字符");
        }
        String loginUserId = body.getLoginUserId();
        if (body.getBackend().booleanValue()) {
            String userId = body.getTargetUserId();
            if (StringUtils.isBlank(userId)) {
                throw new BusinessException(GeneralCode.SYS_VALID);
            }
            String ruleId = body.getRuleId();
            String apiName = body.getApiName();
            String ip = body.getIp();
            AuthTypeEnum operationType = body.getOperationType();
            String info = body.getInfo();
            ApiModel model =
                    this.backendGetApiKey(ruleId, apiName, ip, operationType, info, userId, loginUserId, useOldDB);
            return CopyBeanUtils.copy(model, ApiModelResponse.class);
        }
        if (StringUtils.equals("1", this.sysConfigVarCacheService.getValue("system_maintenance"))) {
            throw new BusinessException(GeneralCode.SYS_MAINTENANCE);
        }
        if (StringUtils.equals("1", this.sysConfigVarCacheService.getValue("user_api_status"))) {
            throw new BusinessException(GeneralCode.SYS_ERROR, "api_msg");
        }
        if (body.getOperationType() == null) {
            throw new BusinessException(GeneralCode.SYS_VALID, "operationType必须是GOOGLE、SMS之一");
        }
        // 验证登录用户
        String uid = loginUserId;
        GetUserResponse userResponse = this.getCheckedUser(uid);
        // 二次验证
        VarificationTwoRequest varificationTwoRequestBody = new VarificationTwoRequest();
        varificationTwoRequestBody.setUserId(Long.parseLong(uid));
        varificationTwoRequestBody.setCode(body.getVerifyCode());
        if (AuthTypeEnum.GOOGLE.equals(body.getOperationType())) {
            varificationTwoRequestBody.setAuthType(AuthTypeEnum.GOOGLE);
        } else if (AuthTypeEnum.SMS.equals(body.getOperationType())) {
            varificationTwoRequestBody.setAuthType(AuthTypeEnum.SMS);
        }
        varificationTwoRequestBody.setAutoDel(true);
        this.getAPIRequestResponse(
                this.userSecurityApi.verificationsTwo(this.newAPIRequest(varificationTwoRequestBody)));

        String json = this.matchboxService.postApiKey(userResponse.getUserInfo().getTradingAccount(),
                "api" + String.valueOf(System.currentTimeMillis()), null, null, null, null, null, !production);
        ApiKeyVo apiKeyVo = MatchboxReturnUtils.getMbxValue(json, ApiKeyVo.class);
        String uuid = UUID.randomUUID().toString().replace("-", "");
        ApiModel apiModel = new ApiModel();
        apiModel.setUuid(uuid);
        apiModel.setUserId(uid);
        apiModel.setApiKey(CryptoUtils.encryptAESToString(apiKeyVo.getApiKey(), this.aesPass));
        apiModel.setKeyId(apiKeyVo.getKeyId().intValue());
        apiModel.setApiName(body.getApiName());
        apiModel.setSecretKey(CryptoUtils.encryptAESToString(apiKeyVo.getSecretKey(), this.aesPass));
        apiModel.setTradeIp(IP);
        apiModel.setWithdrawIp(IP);
        apiModel.setStatus(1);
        apiModel.setEnableWithdrawStatus(false);
        apiModel.setRuleId(String.valueOf(ApiManagerUtils.TRADE));
        apiModel.setInfo(StringUtils.defaultString(body.getInfo(), "mbx-service创建"));
        apiModel.setApiEmailVerify(false);
        apiModel.setCreateEmailSendTime(DateUtils.getNewUTCDate());
        apiModel.setCreateTime(DateUtils.getNewUTCDate());

        this.insertApiModel(apiModel, useOldDB);

        Map<String, Object> emailParams = Maps.newHashMap();
        emailParams.put("apiName", apiModel.getApiName());
        String link = this.getHttpBasePath() + "userCenter/createApi.html?id=" + uuid;
        emailParams.put("link", link);
        try {
            this.sendEmail(Constants.NODE_TYPE_API_CREATE_ENABLE, emailParams, userResponse.getUser().getEmail(), uid);
        } catch (Exception e) {
            log.error("API创建邮件发送失败:", e);
        }

        apiModel.setApiKey(apiKeyVo.getApiKey());
        apiModel.setSecretKey(apiKeyVo.getSecretKey());
        return CopyBeanUtils.copy(apiModel, ApiModelResponse.class);
    }

    private boolean isDoubleWrite() {
        Integer doubWrite = this.sysConfigVarCacheService.getInteger("api_manage_data_double_write");
        return doubWrite != null && doubWrite.intValue() == 1;
    }

    private void insertApiModel(ApiModel apiModel, boolean useOldDB) {
        apiModel.setId(getPrimaryKey());
        if (useOldDB) {
            this.oldApiModelMapper.insertWithId(apiModel);
            this.apiModelMapper.insertWithId(apiModel);
        } else {
            this.apiModelMapper.insertWithId(apiModel);
            log.info("插入ApiManage：{}", JSON.toJSONString(apiModel));
            if (this.isDoubleWrite()) {
                ApiModel model = null;
                HintManager hintManager = null;
                try {
                    hintManager = HintManager.getInstance();
                    hintManager.setMasterRouteOnly();
                    model = this.apiModelMapper.selectById(apiModel.getId());
                } finally {
                    if (null != hintManager) {
                        hintManager.close();
                    }
                }
                JSONObject jsonObject = (JSONObject) JSON.toJSON(model);
                jsonObject.put("theAction", "insert");
                String jsonStr = jsonObject.toJSONString();
                log.info("插入ApiManage：{},通过mq通知Pnk插入数据到Pnk库", jsonStr);
                this.mqToPnk(jsonStr);
            }
        }
    }

    private void updateByPrimaryKey(ApiModel apiManageModel, boolean useOldDB) {
        if (useOldDB) {
            this.oldApiModelMapper.updateByPrimaryKey(apiManageModel);
            this.apiModelMapper.updateByPrimaryKey(apiManageModel);
        } else {
            this.apiModelMapper.updateByPrimaryKey(apiManageModel);
            if (this.isDoubleWrite()) {
                ApiModel model = null;
                HintManager hintManager = null;
                try {
                    hintManager = HintManager.getInstance();
                    hintManager.setMasterRouteOnly();
                    model = this.apiModelMapper.selectById(apiManageModel.getId());
                } finally {
                    if (null != hintManager) {
                        hintManager.close();
                    }
                }
                JSONObject jsonObject = (JSONObject) JSON.toJSON(model);
                jsonObject.put("theAction", "update");
                String jsonStr = jsonObject.toJSONString();
                log.info("更新ApiManage.updateByPrimaryKey：{},通过mq通知Pnk更新Pnk库", jsonStr);
                this.mqToPnk(jsonStr);
            }
        }
    }

    private void updateForApiWithdraw(Map<String, Object> param, boolean useOldDB) {
        if (useOldDB) {
            this.oldApiModelMapper.updateForApiWithdraw(param);
            this.apiModelMapper.updateForApiWithdraw(param);
        } else {
            this.apiModelMapper.updateForApiWithdraw(param);
            if (this.isDoubleWrite()) {
                ApiModel model = null;
                HintManager hintManager = null;
                try {
                    hintManager = HintManager.getInstance();
                    hintManager.setMasterRouteOnly();
                    model = this.apiModelMapper.selectById(Long.parseLong(param.get("id").toString()));
                } finally {
                    if (null != hintManager) {
                        hintManager.close();
                    }
                }
                JSONObject jsonObject = (JSONObject) JSON.toJSON(model);
                jsonObject.put("theAction", "update");
                String jsonStr = jsonObject.toJSONString();
                log.info("更新ApiManage.updateForApiWithdraw：{},通过mq通知Pnk更新Pnk库", jsonStr);
                this.mqToPnk(jsonStr);
            }
        }
    }

    private void updateEmailVerifyStatus(boolean apiEmailVerify, Long id, String userId, boolean useOldDB) {
        if (useOldDB) {
            this.oldApiModelMapper.updateEmailVerifyStatus(true, id, userId);
            this.apiModelMapper.updateEmailVerifyStatus(true, id, userId);
        } else {
            this.apiModelMapper.updateEmailVerifyStatus(true, id, userId);
            if (this.isDoubleWrite()) {
                ApiModel model = null;
                HintManager hintManager = null;
                try {
                    hintManager = HintManager.getInstance();
                    hintManager.setMasterRouteOnly();
                    model = this.apiModelMapper.selectById(id);
                } finally {
                    if (null != hintManager) {
                        hintManager.close();
                    }
                }
                JSONObject jsonObject = (JSONObject) JSON.toJSON(model);
                jsonObject.put("theAction", "update");
                String jsonStr = jsonObject.toJSONString();
                log.info("更新ApiManage.updateEmailVerifyStatus：{},通过mq通知Pnk更新Pnk库", jsonStr);
                this.mqToPnk(jsonStr);
            }
        }
    }

    private void mqToPnk(String jsonApiModel) {
        log.info("mqToPnk message {}", jsonApiModel);
        this.rabbitTemplate.convertAndSend("to_pnk_sync_apimanage", jsonApiModel);
    }

    @MyScheduledJob(cron = "0 0 0 1 12 ? *", author = "wangxiao", alarmEmail = "louis.wang@binance.com",
            jobDesc = "test")
    public void test() {
        if (!this.production) {
            List<ApiModel> list = this.oldApiModelMapper.load(1, Integer.MAX_VALUE, 0);
            ApiModel apiModel = list.get(0);
            apiModel.setId(null);
            long t = System.currentTimeMillis();
            for (int i = 0; i < 10000; i++) {
                this.oldApiModelMapper.insert(apiModel);
            }
            System.out.println("*****************" + (System.currentTimeMillis() - t));
        }
    }

    /**
     * 
     * @param parameter
     */
    @MyScheduledJob(cron = "0 0 0 1 12 ? *", author = "wangxiao", alarmEmail = "louis.wang@binance.com",
            jobDesc = "12月1日执行，目的是近期不会自动执行")
    public void syncApiManageFromPnkOldData(String parameter) {
        if (isSyncFromOldDB) {
            IDistLock lock = this.distLockManager.buildRedDistLock("mbx_api_manage_sync_pnk_lock");
            try {
                if (lock.tryLock(20, -1, TimeUnit.SECONDS)) {
                    long startId = this.initId;
                    // 如果收到重新同步的命令，则清空表，并设置已同步的最大id是0
                    if (StringUtils.isNotBlank(parameter)) {
                        String[] array = StringUtils.split(parameter, ",");
                        if (array.length == 2) {
                            if ("resync".equals(StringUtils.trim(array[0]))) {
                                this.apiModelMapper.clear();
                                redisTemplate.opsForValue().set(MBX_SYNC_API_MANAGE_MAX_ID, 0);
                                startId = Long.parseLong(StringUtils.trim(array[1]));
                            }
                        }
                    }
                    int DELAY = 120;
                    int delay = DELAY;
                    int rows = 800;
                    long t = System.currentTimeMillis();
                    for (;;) {
                        Object syncMaxId = redisTemplate.opsForValue().get(MBX_SYNC_API_MANAGE_MAX_ID);
                        if (syncMaxId == null) {
                            syncMaxId = 0;
                        }
                        List<ApiModel> list =
                                this.oldApiModelMapper.load(rows, startId, Long.parseLong(syncMaxId.toString()));
                        if (list == null) {
                            list = Lists.newArrayList();
                        }
                        if (list.size() > 0) {
                            long maxId = list.get(list.size() - 1).getId();
                            log.info("同步{}条数据到mbx的api_manage表,最大ID是{}", list.size(), maxId);
                            this.apiModelMapper.insertFromPnkData(list);
                            redisTemplate.opsForValue().set(MBX_SYNC_API_MANAGE_MAX_ID, maxId);
                        } else {
                            log.info("没有从pnk库中获取到可用同步的数据");
                        }
                        if (list.size() < rows) {
                            System.out.println("*************************" + (System.currentTimeMillis() - t));
                            if (delay > 1) {
                                delay--;
                                ThreadUtils.sleep(5000);
                            } else {
                                break;
                            }
                        } else {
                            if (delay < DELAY) {
                                delay++;
                            }
                        }
                    }
                }
            } catch (InterruptedException e) {
                log.error("获取锁失败：", e);
            } finally {
                lock.unlock();
            }
        }
    }

    @RabbitListener(queues = "to_mbx_gateway_sync_apimanage")
    public void syncApiManageFromPnkByMQ(String msg, @Header(AmqpHeaders.CHANNEL) Channel channel,
            @Header(AmqpHeaders.DELIVERY_TAG) Long deliveryTag) {
        try {
            if (this.isDoubleWrite()) {
                if (StringUtils.isBlank(msg)) {
                    log.warn("------>syncApiManageFromPnkByMQ receive() The mq msg of received is blank !");
                    return;
                }
                log.info("------>syncApiManageFromPnkByMQ Receive message : {}", msg);
                msg = StringUtils.trim(msg);
                if (StringUtils.startsWith(msg, "\"{") && StringUtils.endsWith(msg, "}\"")) {
                    msg = StringUtils.substringBetween(msg, "\"{", "}\"");
                    msg = "{" + StringUtils.replace(msg, "\\\"", "\"") + "}";
                }
                JSONObject jsonObject = JSON.parseObject(msg);
                String theAction = jsonObject.getString("theAction");
                jsonObject.remove("theAction");
                ApiModel apiModel = jsonObject.toJavaObject(ApiModel.class);
                try {
                    switch (theAction) {
                        case "insert":
                            log.info("syncApiManageFromPnkByMQ insert {}", jsonObject);
                            this.apiModelMapper.insertFromPnkData(Lists.newArrayList(apiModel));
                            break;
                        case "update":
                            log.info("syncApiManageFromPnkByMQ update {}", jsonObject);
                            this.apiModelMapper.updateFromPnkData(apiModel);
                            break;
                        case "delete":
                            log.info("syncApiManageFromPnkByMQ delete {}", jsonObject);
                            this.apiModelMapper.deleteById(apiModel.getId());
                            this.afterDeteApiKey(apiModel);
                            break;
                    }
                } catch (Exception e) {
                    log.error("------>syncApiManageFromPnkByMQ operat db occurs error :{},{}", jsonObject, e);
                    channel.basicReject(deliveryTag, true);
                    return;
                }
            }
            // 手动ack确认
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            log.error("------>syncApiManageFromPnkByMQ receive() Message parsing exception :", e);
            try {
                channel.basicReject(deliveryTag, false);
            } catch (IOException e1) {
                log.error("------>syncApiManageFromPnkByMQ receive() Rabbit channel basicReject error :", e1);
            }
        }
    }

    private ApiModel backendGetApiKey(String ruleId, String apiName, String ip, AuthTypeEnum operationType, String info,
            String userId, String loginUserId, boolean useOldDB) throws Exception {
        if (StringUtils.isBlank(userId)) {
            throw new BusinessException(GeneralCode.SYS_VALID);
        }
        if ("3".equals(ruleId) && StringUtils.isBlank(ip)) {
            throw new BusinessException(GeneralCode.SYS_VALID, "如果可提现，则必须提供IP地址");
        }
        GetUserResponse userResponse = this.getCheckedUser(userId);
        boolean ipStatus = true;
        List<String> ipList = new ArrayList<String>();
        if (StringUtils.isNotBlank(ip)) {
            String ips[] = ip.split(",");
            for (int i = 0; i < ips.length; i++) {
                String ipStr = ips[i];
                Pattern pattern = Pattern.compile(RegexUtils.IP_REGEX);
                Matcher matcher = pattern.matcher(ipStr);
                // 字符串是否与正则表达式相匹配
                if (matcher.matches()) {
                    ipList.add(ipStr);
                } else {
                    ipStatus = false;
                }
            }
        }
        if (!ipStatus) {
            throw new BusinessException(GeneralCode.SYS_VALID, "ip地址错误");
        }
        if (IP.equals(ip)) {
            throw new BusinessException(GeneralCode.SYS_ERROR, "IP地址不能为" + IP);
        }
        String json = this.matchboxService.postApiKey(userResponse.getUserInfo().getTradingAccount(),
                "api" + String.valueOf(System.currentTimeMillis()), null, null, null, null, null, !production);
        ApiKeyVo apiKeyVo = MatchboxReturnUtils.getMbxValue(json, ApiKeyVo.class);
        ApiModel apiModel = new ApiModel();
        apiModel.setUserId(userId);
        apiModel.setApiKey(CryptoUtils.encryptAESToString(apiKeyVo.getApiKey(), this.aesPass));
        apiModel.setKeyId(apiKeyVo.getKeyId().intValue());
        apiModel.setApiName(apiName);
        apiModel.setSecretKey(CryptoUtils.encryptAESToString(apiKeyVo.getSecretKey(), this.aesPass));
        apiModel.setTradeIp("3".equals(ruleId) ? ip : IP);
        apiModel.setWithdrawIp("3".equals(ruleId) ? ip : IP);
        apiModel.setStatus("3".equals(ruleId) ? 2 : 1);
        apiModel.setRuleId(ruleId);
        apiModel.setInfo(StringUtils.defaultString(info, "后台mbx-service创建"));
        apiModel.setCreateTime(DateUtils.getNewUTCDate());

        this.insertApiModel(apiModel, useOldDB);

        apiModel.setApiKey(apiKeyVo.getApiKey());
        apiModel.setSecretKey(apiKeyVo.getSecretKey());

        // 记录操作日志
        try {
            OperateLogModel model = new OperateLogModel();
            model.setId(UUID.randomUUID().toString().replace("-", ""));
            model.setUserId(loginUserId);
            model.setOperateTime(new Date());
            model.setIpAddress(WebUtils.getRequestIp());
            model.setOperateType(Constants.TYPE_ADD);
            model.setOperateModel(Constants.MODEL_TRADE);
            model.setOperateResult(Constants.RESULT_SUCCESS);
            model.setResInfo(
                    String.format("创建API成功：userId:%s,ruleId:%s,apiKey:%s,", userId, ruleId, apiKeyVo.getApiKey()));
            this.operateLogModelMapper.insert(model);
        } catch (Exception e) {
            log.error("创建API后记录操作日志出现异常", e);
        }
        return apiModel;
    }

    private void sendEmail(String tplCode, Map<String, Object> params, String email, String userId) throws Exception {
        String language = LanguageUtils.getLanguage(WebUtils.getHttpServletRequest());
        UserIdRequest body = new UserIdRequest();
        body.setUserId(Long.parseLong(userId));
        if (StringUtils.isNotBlank(userId)) {
            try {
                UserSecurityLogVo userSecurityLogVo =
                        this.getAPIRequestResponse(this.userSecurityLogApi.getLastLoginLog(this.newAPIRequest(body)));
                String ipLocation = userSecurityLogVo.getIpLocation();
                if (ipLocation != null) {
                    if (ipLocation.endsWith(" China")) {
                        language = "cn";
                    } else {
                        language = "en";
                    }
                }
            } catch (Exception e) {
                log.warn("获取用户最后一次登录信息出错", e);
            }
        }
        if (!("en").equals(language) && !("cn").equals(language)) {
            language = "en";
        }
        String ip = WebUtils.getRequestIp();
        params.put("ip", ip);
        params.put("time", DateUtils.getNewDateUTC() + " (UTC)");

        String emailVerifyCode =
                this.getAPIRequestResponse(this.userSecurityApi.generateForbidCode(this.newAPIRequest(body)));
        String forbiddenLink = this.getHttpBasePath() + "forbiddenAccount.html?userId=" + userId + "&emailVerifyCode="
                + emailVerifyCode;
        params.put("userId", userId);
        params.put("email", email);
        params.put("emailVerifyCode", emailVerifyCode);
        params.put("forbiddenLink", forbiddenLink);

        String antiCode =
                this.getAPIRequestResponse(this.userSecurityApi.selectAntiPhishingCode(this.newAPIRequest(body)));
        if (StringUtils.isNotBlank(antiCode)) {
            params.put("antiCode", antiCode);
        }
        // UserForbiddenCodeModel userForbiddenCode = new UserForbiddenCodeModel();
        // userForbiddenCode.setUserId(userId);
        // userForbiddenCode.setEmailVerifyCode(emailVerifyCode);
        // this.userForbiddenCodeModelMapper.insert(userForbiddenCode);
        SendMsgRequest sendMsgBody = new SendMsgRequest();
        sendMsgBody.setTplCode(tplCode);
        sendMsgBody.setAntiPhishingCode((String) params.get("antiCode"));
        sendMsgBody.setRecipient(email);
        sendMsgBody.setUserId(userId);
        sendMsgBody.setData(params);

        HeaderUtils.set("ip-address", WebUtils.getRequestIp());
        HeaderUtils.set("base-url", this.getHttpBasePath());
        HeaderUtils.set("base-brower", WebUtils.getOsAndBrowserInfo());
        APIRequest<SendMsgRequest> apiRequest = this.newAPIRequest(sendMsgBody, language);
        TerminalEnum terminal = WebUtils.getTerminal();
        if (terminal == null) {
            terminal = TerminalEnum.WEB;
        }
        apiRequest.setTerminal(terminal);
        String version = WebUtils.getHeader("versionCode");
        if (StringUtils.isBlank(version)) {
            version = WebUtils.getParameter("versionCode");
        }
        if (StringUtils.isNotBlank(version)) {
            apiRequest.setVersion(version);
        }
        this.msgApi.sendMsg(apiRequest);
    }

    @Override
    public void deleteApiKey(DeleteApiKeyRequest deleteApiKeyRequest) throws Exception {
        boolean useOldDB = this.isUseOldDB;
        if (StringUtils.equals("1", this.sysConfigVarCacheService.getValue(Constants.SYSTEM_MAINTENANCE))) {
            throw new BusinessException(GeneralCode.SYS_MAINTENANCE);
        }
        String loginUserId = deleteApiKeyRequest.getLoginUid();
        GetUserResponse user = this.getCheckedUser(loginUserId);
        UserInfoVo userInfo = user.getUserInfo();
        Map<String, Object> param = new HashMap<String, Object>();
        param.put("id", deleteApiKeyRequest.getId());
        param.put("userId", loginUserId);
        if (StringUtils.isNotBlank(deleteApiKeyRequest.getApiKey())) {
            param.put("apiKey", CryptoUtils.encryptAESToString(deleteApiKeyRequest.getApiKey(), this.aesPass));
        }
        List<ApiModel> apiModels =
                useOldDB ? this.oldApiModelMapper.getApiByMap(param) : this.apiModelMapper.getApiByMap(param);
        if (apiModels == null || apiModels.isEmpty()) {
            throw new BusinessException(GeneralCode.API_KEY_NOT_FOUND);
        }
        ApiModel apiModel = apiModels.get(0);
        this.deleteApiKey(loginUserId, userInfo, apiModel, useOldDB);
    }

    private void deleteApiKey(String loginUserId, UserInfoVo userInfo, ApiModel apiModel, boolean useOldDB)
            throws Exception {
        if (getApiExsit(userInfo.getTradingAccount(), apiModel.getKeyId())) {
            this.matchboxService.deleteApiKey(userInfo.getTradingAccount(), apiModel.getKeyId());
            if (useOldDB) {
                this.oldApiModelMapper.deleteById(apiModel.getId());
                this.apiModelMapper.deleteById(apiModel.getId());
            } else {
                this.apiModelMapper.deleteById(apiModel.getId());
            }
            // 记录操作日志
            operateLogService.insert(loginUserId, "api", Constants.RESULT_SUCCESS, Constants.TYPE_DELETE,
                    String.format(("用户:%s,删除apiKey:%s成功"), loginUserId,
                            CryptoUtils.decryptAESToString(apiModel.getApiKey(), this.aesPass)));
        } else {
            if (useOldDB) {
                this.oldApiModelMapper.deleteById(apiModel.getId());
                this.apiModelMapper.deleteById(apiModel.getId());
            } else {
                this.apiModelMapper.deleteById(apiModel.getId());
            }
            // 记录操作日志
            operateLogService.insert(loginUserId, "api", Constants.RESULT_SUCCESS, Constants.TYPE_DELETE,
                    String.format(("用户:%s,删除apiKey:%s成功"), loginUserId,
                            CryptoUtils.decryptAESToString(apiModel.getApiKey(), this.aesPass)));
        }
        if (!useOldDB && this.isDoubleWrite()) {
            JSONObject jsonObject = (JSONObject) JSON.toJSON(apiModel);
            jsonObject.put("theAction", "delete");
            String jsonStr = jsonObject.toJSONString();
            log.info("删除ApiManage：{},通过mq通知Pnk删除Pnk库中相应数据", jsonStr);
            this.mqToPnk(jsonStr);
        }
        this.afterDeteApiKey(apiModel);
    }

    private void afterDeteApiKey(ApiModel apiModel) throws Exception {
        try {
            ApiDeletedModel deletedModel = CopyBeanUtils.copy(apiModel, ApiDeletedModel.class);
            deletedModel.setOriginalId(apiModel.getId());
            deletedModel.setId(null);
            try {
                deletedModel.setEmail(this.getUser(apiModel.getUserId()).getUser().getEmail());
            } catch (Exception e) {
                log.warn("afterDeteApiKey occurs error:", e);
            }
            this.apiDeletedModelMapper.insert(deletedModel);
        } catch (Exception e) {
            log.error("保存删除的api出错：{},{}", apiModel == null ? null : JSON.toJSONString(apiModel), e);
        }
    }

    @Override
    public void deleteAllApiKey(DeleteAllApiKeyRequest body) throws Exception {
        boolean useOldDB = this.isUseOldDB;
        if (StringUtils.equals("1", this.sysConfigVarCacheService.getValue(Constants.SYSTEM_MAINTENANCE))) {
            throw new BusinessException(GeneralCode.SYS_MAINTENANCE);
        }
        String loginUserId = body.getLoginUid();
        GetUserResponse user = this.getCheckedUser(loginUserId);
        UserInfoVo userInfo = user.getUserInfo();
        Map<String, Object> param = new HashMap<String, Object>();
        param.put("userId", loginUserId);
        List<ApiModel> apiModels =
                useOldDB ? this.oldApiModelMapper.getApiByMap(param) : this.apiModelMapper.getApiByMap(param);
        if (apiModels == null || apiModels.isEmpty()) {
            throw new BusinessException(GeneralCode.API_KEY_NOT_FOUND);
        }
        for (ApiModel apiModel : apiModels) {
            this.deleteApiKey(loginUserId, userInfo, apiModel, useOldDB);
        }
    }

    private Boolean getApiExsit(Long accountId, Integer keyId) {
        try {
            Boolean status = false;
            String json = this.matchboxService.getApiKeys(accountId);
            if (json != null) {
                JSONArray jsonArray = JSON.parseArray(json);
                for (int i = 0; i < jsonArray.size(); i++) {
                    JSONObject jsonObject = jsonArray.getJSONObject(i);
                    if (jsonObject.getIntValue("keyId") == keyId) {
                        return true;
                    }
                }
            }
            return status;
        } catch (Exception e) {
            return true;
        }
    }

    @Override
    public UpdateApiKeyResponse updateApiKey(UpdateApiKeyRequest request) throws Exception {
        boolean useOldDB = this.isUseOldDB;
        log.info("updateApiRequest:{}", request.toString());
        UpdateApiKeyResponse response = new UpdateApiKeyResponse();
        try {
            if (StringUtils.equals("1", this.sysConfigVarCacheService.getValue("system_maintenance"))) {
                throw new BusinessException(GeneralCode.SYS_MAINTENANCE);
            }
            if (StringUtils.equals("1", this.sysConfigVarCacheService.getValue("user_api_status"))) {
                throw new BusinessException(GeneralCode.SYS_ERROR, "api_msg");
            }
            Long id = request.getId();
            String userId = request.getUserId();
            String ip = request.getIp();
            Integer ruleId = request.getRuleId();
            GetUserResponse userResponse = this.getCheckedUser(userId);
            UserInfoVo userInfo = userResponse.getUserInfo();

            // 勾选了"开放提现"或"内部划转"
            if (ApiManagerUtils.isWithdrawEnabled(Long.valueOf(ruleId))
                    || ApiManagerUtils.isInternalTransferEnabled(Long.valueOf(ruleId))) {
                // 子账户禁止使用该功能
                UserIdReq userIdReq = new UserIdReq();
                userIdReq.setUserId(Long.parseLong(userId));
                Boolean checkResp = this.getAPIRequestResponse(
                        this.subUserApi.notSubUserOrIsEnabledSubUser(this.newAPIRequest(userIdReq)));
                if (!BooleanUtils.isTrue(checkResp)) {
                    throw new BusinessException("子账户禁止使用该功能");
                }
            }

            this.vaildate(userId, request.getVerifyCode(), request.getOperationType());
            Map<String, Object> param = new HashMap<String, Object>();
            param.put("id", id);
            param.put("userId", userId);
            List<ApiModel> apiManageModels =
                    useOldDB ? this.oldApiModelMapper.getApiByMap(param) : this.apiModelMapper.getApiByMap(param);
            if (apiManageModels == null || apiManageModels.isEmpty()) {
                throw new BusinessException(GeneralCode.KEY_API_KEY_NOT_EXIST);
            }
            ApiModel apiManageModel = apiManageModels.get(0);
            if (!apiManageModel.isApiEmailVerify()) {
                throw new BusinessException(GeneralCode.API_NOT_BEEN_VERIFY);
            }
            Map<String, Object> ruleMap = getRuleMap(userInfo.getTradingAccount(), apiManageModel.getKeyId());
            Map<String, Object> emailParams = Maps.newHashMap();
            if (request.getStatus() == 1) {
                ip = IP;
            } else {
                if (StringUtils.isBlank(ip)) {
                    throw new BusinessException(GeneralCode.SYS_VALID);
                }
            }
            boolean ipStatus = true;
            List<String> ipList = new ArrayList<String>();
            if (StringUtils.isNotEmpty(ip)) {
                String ips[] = ip.split(",");
                for (int i = 0; i < ips.length; i++) {
                    String ipStr = ips[i];
                    Pattern pattern = Pattern.compile(RegexUtils.IP_REGEX);
                    Matcher matcher = pattern.matcher(ipStr);
                    // 字符串是否与正则表达式相匹配
                    if (matcher.matches()) {
                        ipList.add(ipStr);
                    } else {
                        ipStatus = false;
                    }
                }
            }
            if (!ipStatus) {
                throw new BusinessException(GeneralCode.API_KEY_UPDATE_IP_ILLEGEL);
            }
            // 提现时ip必须除了0.0.0.0还有其他的地址
            if (ApiManagerUtils.isWithdrawEnabled(Long.valueOf(ruleId))) {
                if (ipList.size() == 1 && ip.contains(IP)) {
                    throw new BusinessException(GeneralCode.API_KEY_UPDATE_IP_APPOINT);
                }
            }

            for (int i = 0; i < ipList.size(); i++) {
                String ipItem = ipList.get(i);
                if (ruleMap.get(ipItem) == null) {
                    this.matchboxService.postApiKeyRule(userInfo.getTradingAccount(), ipItem,
                            apiManageModel.getKeyId());
                    log.info("updateApi  --saveApiRule:{}", ipItem);
                } else {
                    ruleMap.remove(ipItem);
                }
            }

            log.info("updateApi...ip end");

            for (Map.Entry<String, Object> entry : ruleMap.entrySet()) {
                this.matchboxService.deleteApiKeyRule(userInfo.getTradingAccount(), apiManageModel.getKeyId(),
                        entry.getValue());
            }

            log.info("updateApi...deleteRuleByRuleId end");

            if (!apiManageModel.getRuleId().equals(String.valueOf(ruleId))) {
                // 是否可以交易权限
                if (ApiManagerUtils.isTradeEnabled(Long.valueOf(ruleId))) {
                    this.matchboxService.putApiKeyPermissions(userInfo.getTradingAccount(), true, true, true, true,
                            true, apiManageModel.getKeyId(), true);
                } else {
                    this.matchboxService.putApiKeyPermissions(userInfo.getTradingAccount(), true, true, false, true,
                            true, apiManageModel.getKeyId(), true);
                }
            }

            String preRuleId = apiManageModel.getRuleId();
            apiManageModel.setApiName(request.getApiName());
            apiManageModel.setInfo(request.getInfo());
            apiManageModel.setTradeIp(ip);
            apiManageModel.setStatus(request.getStatus());
            apiManageModel.setRuleId(String.valueOf(ruleId));
            apiManageModel.setWithdrawIp(ip);
            apiManageModel.setUpdateTime(new Date());

            if (ApiManagerUtils.isWithdrawEnabled(Long.valueOf(ruleId))) {
                if (!apiManageModel.isEnableWithdrawStatus()) {
                    String vCode = UUID.randomUUID().toString().replace("-", "");
                    apiManageModel.setEnableWithdrawStatus(false);
                    apiManageModel.setWithdrawVerifycode(vCode);
                    apiManageModel.setWithdrawVerifycodeTime(new Date());
                    emailParams.put("apiName", request.getApiName());
                    String link = this.getHttpBasePath()
                            + "gateway-api/v1/public/api-mgmt/api/enable-api-withdraw?verifyCode=" + vCode;
                    emailParams.put("link", link);
                    response.setFrontendTip(this.messageHelper.getMessage(GeneralCode.API_KEY_WITHDRAW_EAMIL_ENABEL));
                }
            } else {
                apiManageModel.setEnableWithdrawStatus(false);
                apiManageModel.setWithdrawVerifycode(null);
                apiManageModel.setWithdrawVerifycodeTime(null);
            }
            this.updateByPrimaryKey(apiManageModel, useOldDB);
            log.info("updateApi...preRuleId:{}", preRuleId);
            if (ApiManagerUtils.isWithdrawEnabled(Long.valueOf(ruleId)) && !apiManageModel.isEnableWithdrawStatus()) {
                try {
                    this.sendEmail(Constants.NODE_TYPE_API_WITHDRAW_ENABLE, emailParams,
                            userResponse.getUser().getEmail(), userId);
                } catch (Exception e) {
                    log.error("API提现确认邮件发送失败:", e);
                }
            }
            response.setUserId(request.getUserId());
            return response;
        } catch (HttpStatusCodeException e) {
            String responseStr = e.getResponseBodyAsString();
            if ("{\"code\":400,\"msg\":\"删除读取权限失败\"}".equals(responseStr)) {
                log.info("删除API无权限");
                throw new BusinessException(GeneralCode.API_KEY_UPDATE_FAIL);
            } else {
                log.error("修改api失败:", e);
                throw new BusinessException(GeneralCode.API_KEY_UPDATE_FAIL);
            }
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("修改api失败:", e);
            throw new BusinessException(GeneralCode.API_KEY_UPDATE_FAIL);
        }
    }

    private Map<String, Object> getRuleMap(long accountId, Integer keyId) {
        Map<String, Object> resultMap = new HashMap<String, Object>();
        String resultStr = this.matchboxService.getApiKeys(accountId);
        if (StringUtils.isNotEmpty(resultStr)) {
            JSONArray jsonArray = JSONArray.parseArray(resultStr);
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                if (Objects.equals(jsonObject.getInteger("keyId"), keyId)) {
                    JSONArray ruleArray = null;
                    try {
                        ruleArray = jsonObject.getJSONArray("rules");
                    } catch (Exception e) {
                        ruleArray = jsonObject.getJSONArray("rules:");
                    }
                    for (int j = 0; j < ruleArray.size(); j++) {
                        JSONObject ruleObject = ruleArray.getJSONObject(j);
                        resultMap.put(ruleObject.getString("ip"), ruleObject.getInteger("ruleId"));
                    }
                }
            }
        }
        return resultMap;
    }

    private void vaildate(String uid, String verifyCode, AuthTypeEnum operationType) throws Exception {
        // 二次验证
        VarificationTwoRequest varificationTwoRequestBody = new VarificationTwoRequest();
        varificationTwoRequestBody.setUserId(Long.parseLong(uid));
        varificationTwoRequestBody.setCode(verifyCode);
        if (AuthTypeEnum.GOOGLE.equals(operationType)) {
            varificationTwoRequestBody.setAuthType(AuthTypeEnum.GOOGLE);
        } else if (AuthTypeEnum.SMS.equals(operationType)) {
            varificationTwoRequestBody.setAuthType(AuthTypeEnum.SMS);
        }
        varificationTwoRequestBody.setAutoDel(true);
        this.getAPIRequestResponse(
                this.userSecurityApi.verificationsTwo(this.newAPIRequest(varificationTwoRequestBody)));
    }

    @Override
    public ApiModelResponse enableApiCreate(EnableApiCreateRequest body) throws Exception {
        boolean useOldDB = this.isUseOldDB;
        if (StringUtils.equals("1", this.sysConfigVarCacheService.getValue(Constants.SYSTEM_MAINTENANCE))) {
            throw new BusinessException(GeneralCode.SYS_MAINTENANCE);
        }
        if (StringUtils.equals("1", this.sysConfigVarCacheService.getValue("user_api_status"))) {
            throw new BusinessException(GeneralCode.SYS_ERROR, "api_msg");
        }
        ApiModel apiManageModel = useOldDB ? this.oldApiModelMapper.selectModelByUuid(body.getUuid())
                : this.apiModelMapper.selectModelByUuid(body.getUuid());
        if (apiManageModel == null) {
            throw new BusinessException(GeneralCode.API_ENABLEAPIWITHDRAW_NOT_EXIST);
        } else if (apiManageModel.isApiEmailVerify()) {
            throw new BusinessException(GeneralCode.API_HAVE_BEEN_VERIFY);
        } else if (!apiManageModel.isDisableStatus()) {
            throw new BusinessException(GeneralCode.API_ENABLEAPIWITHDRAW_DISABLE);
        } else {
            Long tempTime = DateUtils.getNewUTCTimeMillis() - apiManageModel.getCreateEmailSendTime().getTime();
            if (tempTime > (30 * 60 * 1000L)) {
                throw new BusinessException(GeneralCode.API_ENABLEAPIWITHDRAW_TIME_FAIL);
            }
            apiManageModel.setApiEmailVerify(true);
            this.updateEmailVerifyStatus(true, apiManageModel.getId(), apiManageModel.getUserId(), useOldDB);
            apiManageModel.setSecretKey(CryptoUtils.decryptAESToString(apiManageModel.getSecretKey(), this.aesPass));
            apiManageModel.setApiKey(CryptoUtils.decryptAESToString(apiManageModel.getApiKey(), this.aesPass));
            return CopyBeanUtils.copy(apiManageModel, ApiModelResponse.class);
        }
    }

    @Override
    public void reSendMailVerify(ReSendMailVerifyRequest body) throws Exception {
        boolean useOldDB = this.isUseOldDB;
        if (StringUtils.equals("1", this.sysConfigVarCacheService.getValue(Constants.SYSTEM_MAINTENANCE))) {
            throw new BusinessException(GeneralCode.SYS_MAINTENANCE);
        }
        if (StringUtils.equals("1", this.sysConfigVarCacheService.getValue("user_api_status"))) {
            throw new BusinessException(GeneralCode.SYS_ERROR, "api_msg");
        }
        String email = this.getUser(body.getLoginUid()).getUser().getEmail();
        ApiModel apiManageModel = useOldDB ? this.oldApiModelMapper.selectById(body.getId())
                : this.apiModelMapper.selectById(body.getId());
        if (apiManageModel == null) {
            throw new BusinessException(GeneralCode.API_KEY_NOT_FOUND);
        } else if (apiManageModel.isApiEmailVerify()) {
            throw new BusinessException(GeneralCode.API_KEY_HAVE_BEEN_VERIFIED);
        } else if (apiManageModel.isDisableStatus()) {
            throw new BusinessException(GeneralCode.API_KEY_DISABLED);
        } else {
            Long tempTime = new Date().getTime() - apiManageModel.getCreateEmailSendTime().getTime();
            if (tempTime < (5 * 60 * 1000L)) {
                throw new BusinessException(GeneralCode.SYS_ERROR, "api.enableApiWithdraw.reSendMail.time_fail");
            } else {
                Map<String, Object> param = Maps.newHashMap();
                param.put("id", apiManageModel.getId());
                this.updateForApiWithdraw(param, useOldDB);
                Map<String, Object> emailParams = Maps.newHashMap();
                emailParams.put("apiName", apiManageModel.getApiName());
                String link = this.getHttpBasePath() + "userCenter/createApi.html?id=" + apiManageModel.getUuid();
                emailParams.put("link", link);
                this.sendEmail(Constants.NODE_TYPE_API_CREATE_ENABLE, emailParams, email, body.getLoginUid());
            }
        }
    }

    @Override
    public EnableApiWithdrawResponse enableApiWithdraw(EnableApiWithdrawRequest body) throws Exception {
        boolean useOldDB = this.isUseOldDB;
        if (StringUtils.equals("1", this.sysConfigVarCacheService.getValue(Constants.SYSTEM_MAINTENANCE))) {
            throw new BusinessException(GeneralCode.SYS_MAINTENANCE);
        }
        if (StringUtils.equals("1", this.sysConfigVarCacheService.getValue("user_api_status"))) {
            throw new BusinessException(GeneralCode.SYS_ERROR, "api_msg");
        }
        Map<String, Object> resultMap = new HashMap<String, Object>();
        String type = "3";
        ApiModel apiManageModel = useOldDB ? this.oldApiModelMapper.selectByWithdrawVerifycode(body.getVerifyCode())
                : this.apiModelMapper.selectByWithdrawVerifycode(body.getVerifyCode());
        if (apiManageModel == null) {
            resultMap.put("success", false);
            resultMap.put("msg", GeneralCode.API_ENABLEAPIWITHDRAW_NOT_EXIST);
        } else if (!apiManageModel.isApiEmailVerify()) {
            throw new BusinessException(GeneralCode.API_NOT_BEEN_VERIFY);
        } else if (apiManageModel.isEnableWithdrawStatus()) {
            resultMap.put("success", false);
            resultMap.put("msg", GeneralCode.API_ENABLEAPIWITHDRAW_ENABLED);
            type = "4";
        } else if (!apiManageModel.isDisableStatus()) {
            resultMap.put("success", false);
            resultMap.put("msg", GeneralCode.API_ENABLEAPIWITHDRAW_DISABLE);
        } else {
            Long tempTime = System.currentTimeMillis() - apiManageModel.getWithdrawVerifycodeTime().getTime();
            if (tempTime > (30 * 60 * 1000L)) {
                resultMap.put("success", false);
                resultMap.put("msg", GeneralCode.API_ENABLEAPIWITHDRAW_TIME_FAIL);
                type = "4";
            }
            Map<String, Object> param = Maps.newHashMap();
            param.put("id", apiManageModel.getId());
            param.put("enableWithdrawStatus", true);
            param.put("preEnableWithdrawStatus", false);
            param.put("disableStatus", 1);
            param.put("updateTime", DateUtils.getNewDateUTC());
            param.put("withdrawVerifycode", ""); // 验证完成之后，清空verifyCode
            this.updateForApiWithdraw(param, useOldDB);
            resultMap.put("success", true);
            resultMap.put("msg", GeneralCode.API_ENABLEAPIWITHDRAW_SUCCESS);
        }
        // String redirect = "/withdrawVerify.html?type=" + type;
        // try {
        // WebUtils.getHttpServletResponse().sendRedirect(redirect);
        // } catch (IOException e) {
        // log.error("sendRedirectError", e);
        // }
        EnableApiWithdrawResponse response = new EnableApiWithdrawResponse();
        response.setType(type);
        if (resultMap.get("success") != null && !((Boolean) resultMap.get("success"))) {
            throw new BusinessException((GeneralCode) resultMap.get("msg"));
        }
        return response;
    }

    @Override
    public void reSendMail(ReSendMailRequest body) throws Exception {
        boolean useOldDB = this.isUseOldDB;
        if (StringUtils.equals("1", this.sysConfigVarCacheService.getValue(Constants.SYSTEM_MAINTENANCE))) {
            throw new BusinessException(GeneralCode.SYS_MAINTENANCE);
        }
        GetUserResponse userResponse = this.getCheckedUser(body.getLoginUid());
        ApiModel apiManageModel = useOldDB ? this.oldApiModelMapper.selectById(body.getId())
                : this.apiModelMapper.selectById(body.getId());
        if (apiManageModel == null) {
            throw new BusinessException("api not exist");
        }
        String vCode = apiManageModel.getWithdrawVerifycode();
        if (StringUtils.isEmpty(vCode)) {
            throw new BusinessException("api enableWithdrawVerifyCode not exist");
        } else if (!apiManageModel.isApiEmailVerify()) {
            throw new BusinessException("api_not_been_verify");
        } else if (apiManageModel.isEnableWithdrawStatus()) {
            throw new BusinessException("api.enableApiWithdraw.enabled");
        } else if (apiManageModel.isDisableStatus()) {
            throw new BusinessException(GeneralCode.API_KEY_DISABLED);
        } else {
            Long tempTime = new Date().getTime() - apiManageModel.getWithdrawVerifycodeTime().getTime();
            if (tempTime < (5 * 60 * 1000L)) {
                throw new BusinessException("api.enableApiWithdraw.reSendMail.time_fail");
            } else {
                Map<String, Object> param = Maps.newHashMap();
                param.put("id", apiManageModel.getId());
                param.put("withdrawVerifycodeTime", new Date());
                param.put("disableStatus", 1);
                param.put("preEnableWithdrawStatus", 0);
                this.updateForApiWithdraw(param, useOldDB);
                Map<String, Object> emailParams = Maps.newHashMap();
                emailParams.put("apiName", apiManageModel.getApiName());
                String link = this.getHttpBasePath() + "manageApi/enableApiWithdraw.html?code=" + vCode;
                emailParams.put("link", link);
                this.sendEmail(Constants.NODE_TYPE_API_WITHDRAW_ENABLE, emailParams, userResponse.getUser().getEmail(),
                        body.getLoginUid());
            }
        }
    }

    @Override
    public List<ApiModelResponse> getApis(GetApisRequest body) throws Exception {
        GetApiListRequest getApiListRequest = new GetApiListRequest();
        getApiListRequest.setUserId(body.getUserId());
        return this.getApiList(getApiListRequest);
    }

    @Override
    public List<ApiModelResponse> getApiList(GetApiListRequest body) throws Exception {
        boolean useOldDB = this.isUseOldDB;
        List<ApiModelResponse> list = Lists.newArrayList();
        Map<String, Object> param = new HashMap<String, Object>();
        param.put("id", body.getId());
        param.put("userId", body.getUserId());
        if(StringUtils.isNotBlank(body.getApiKey())){
            param.put("apiKey", CryptoUtils.encryptAESToString(body.getApiKey(), this.aesPass));
        }
        param.put("apiName", body.getApiName());
        List<ApiModel> apiManageModels =
                useOldDB ? this.oldApiModelMapper.getApiByMap(param) : this.apiModelMapper.getApiByMap(param);
        if (apiManageModels == null) {
            return list;
        } else {
            for (ApiModel apiModel : apiManageModels) {
                apiModel.setApiKey(CryptoUtils.decryptAESToString(apiModel.getApiKey(), this.aesPass));
                apiModel.setSecretKey("--");
                apiModel.setWithdrawVerifycode(null);
                apiModel.setWithdraw((ApiManagerUtils.isWithdrawEnabled(Long.valueOf(apiModel.getRuleId())))
                        && (apiModel.isEnableWithdrawStatus()) ? true : false);
                list.add(CopyBeanUtils.copy(apiModel, ApiModelResponse.class));
            }
            return list;
        }
    }

    @Override
    public void disableApiById(DisableApiByIdRequest body) throws Exception {
        boolean useOldDB = this.isUseOldDB;
        if (StringUtils.equals("1", this.sysConfigVarCacheService.getValue(Constants.SYSTEM_MAINTENANCE))) {
            throw new BusinessException(GeneralCode.SYS_MAINTENANCE);
        }
        this.getCheckedUser(body.getLoginUid());
        UserIdRequest userIdRequest = new UserIdRequest();
        userIdRequest.setUserId(Long.parseLong(body.getLoginUid()));
        VarificationTwoRequest varificationTwoRequest = new VarificationTwoRequest();
        varificationTwoRequest.setAuthType(AuthTypeEnum.GOOGLE);
        varificationTwoRequest.setCode(body.getVerifyCode());
        varificationTwoRequest.setUserId(Long.parseLong(body.getLoginUid()));
        varificationTwoRequest.setAutoDel(false);
        this.getAPIRequestResponse(this.userSecurityApi.verificationsTwo(this.newAPIRequest(varificationTwoRequest)));

        Map<String, Object> param = new HashMap<String, Object>();
        param.put("userId", body.getLoginUid());
        param.put("id", body.getId());
        List<ApiModel> apiModels =
                useOldDB ? this.oldApiModelMapper.getApiByMap(param) : this.apiModelMapper.getApiByMap(param);
        if (apiModels == null || apiModels.isEmpty()) {
            throw new BusinessException(GeneralCode.API_KEY_NOT_FOUND);
        }
        ApiModel apiModel = apiModels.get(0);
        UpdateApiKeyRequest updateApiKeyRequest = new UpdateApiKeyRequest();
        updateApiKeyRequest.setApiName(apiModel.getApiName());
        updateApiKeyRequest.setId(apiModel.getId());
        updateApiKeyRequest.setInfo("");
        updateApiKeyRequest.setIp("");
        this.updateApiKey(updateApiKeyRequest);
        apiModel.setDisableStatus(body.getStatus());
        this.updateByPrimaryKey(apiModel, useOldDB);
    }

    public static class ApiManagerUtils {

        // 开放交易
        public static final long TRADE = 1L;
        // 开放提现
        public static final long WITHDRAW = 2L << 0;
        // 内部划转
        public static final long INTERNAL_TRANSFER = 2L << 1;

        public static boolean isTradeEnabled(Long ruleId) {
            return BitUtils.isEnable(ruleId, ApiManagerUtils.TRADE);
        }

        public static boolean isWithdrawEnabled(Long ruleId) {
            return BitUtils.isEnable(ruleId, ApiManagerUtils.WITHDRAW);
        }

        public static boolean isInternalTransferEnabled(Long ruleId) {
            return BitUtils.isEnable(ruleId, ApiManagerUtils.INTERNAL_TRANSFER);
        }

    }
    private static class CryptoUtils {
        /**
         * 将二进制转换成16进制
         *
         * @param buf
         * @return
         */
        public static String parseByte2HexStr(byte buf[]) {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < buf.length; i++) {
                String hex = Integer.toHexString(buf[i] & 0xFF);
                if (hex.length() == 1) {
                    hex = '0' + hex;
                }
                sb.append(hex.toUpperCase());
            }
            return sb.toString();
        }

        /**
         * 将16进制转换为二进制
         *
         * @param hexStr
         * @return
         */
        public static byte[] parseHexStr2Byte(String hexStr) {
            if (hexStr.length() < 1) {
                return null;
            }
            byte[] result = new byte[hexStr.length() / 2];
            for (int i = 0; i < hexStr.length() / 2; i++) {
                int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
                int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
                result[i] = (byte) (high * 16 + low);
            }
            return result;
        }

        public static String encryptAESToString(String conent, String pass) {
            byte[] buf = EncryptionUtils.encryptAES(conent, pass);
            return parseByte2HexStr(buf);
        }

        public static String decryptAESToString(String conent, String pass) {
            byte[] buf = EncryptionUtils.decryptAES(parseHexStr2Byte(conent), pass);
            return new String(buf, Charset.forName("UTF-8"));
        }
    }
}
